﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
using EnvDTE100;
using EnvDTE80;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using SmartStringResources.Dte;

namespace SmartStringResources.VsPackage
{
	/// <summary>
	/// This is the class that implements the package exposed by this assembly.
	///
	/// The minimum requirement for a class to be considered a valid package for Visual Studio
	/// is to implement the IVsPackage interface and register itself with the shell.
	/// This package uses the helper classes defined inside the Managed Package Framework (MPF)
	/// to do it: it derives from the Package class that provides the implementation of the 
	/// IVsPackage interface and uses the registration attributes defined in the framework to 
	/// register itself and its components with the shell.
	/// </summary>
	// This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
	// a package.
	[PackageRegistration(UseManagedResourcesOnly = true)]
	// This attribute is used to register the information needed to show this package
	// in the Help/About dialog of Visual Studio.
	[InstalledProductRegistration("#110", "#112", "1.0.0", IconResourceID = 400)]
	[Guid(GuidList.guidSmartStringResourcesVSPackagePkgString)]
	[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)]
	[ProvideOptionPage(typeof(Options), "Smart String Resources", "General", 101, 106, true)]
	public sealed class SmartStringResourcesPackage : Package
	{
		private DocumentEvents _documentEvents;
		private ProjectItemsEvents _cSharpItemsEvents;
		private ProjectItemsEvents _vbItemsEvents;
		private Solution4 _solution;
		private SourceControl _sourceControl;

		/// <summary>
		/// Default constructor of the package.
		/// Inside this method you can place any initialization code that does not require 
		/// any Visual Studio service because at this point the package object is created but 
		/// not sited yet inside Visual Studio environment. The place to do all the other 
		/// initialization is the Initialize method.
		/// </summary>
		public SmartStringResourcesPackage()
		{
			Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
		}

		protected override void OnLoadOptions(string key, Stream stream)
		{
			base.OnLoadOptions(key, stream);
		}

		/////////////////////////////////////////////////////////////////////////////
		// Overridden Package Implementation
		#region Package Members

		/// <summary>
		/// Initialization of the package; this method is called right after the package is sited, so this is the place
		/// where you can put all the initialization code that rely on services provided by VisualStudio.
		/// </summary>
		protected override void Initialize()
		{
			Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
			base.Initialize();

			var dte = (DTE)GetGlobalService(typeof(DTE));
			_vbItemsEvents = (ProjectItemsEvents)dte.Events.GetObject("VBProjectItemsEvents");
			_cSharpItemsEvents = (ProjectItemsEvents)dte.Events.GetObject("CSharpProjectItemsEvents");
			_solution = (Solution4)dte.Solution;
			_sourceControl = dte.SourceControl;
			_cSharpItemsEvents.ItemAdded += _cSharpItemsEvents_ItemAdded;
			_vbItemsEvents.ItemAdded += _vbItemsEvents_ItemAdded;
			_documentEvents = dte.Events.DocumentEvents;
			_documentEvents.DocumentSaved += DocumentEvents_DocumentSaved;
		}

		void _vbItemsEvents_ItemAdded(ProjectItem projectItem)
		{
			ItemAdded(projectItem, GetVisualBasicProjectItemTemplate());
		}

		private string GetVisualBasicProjectItemTemplate()
		{
			return _solution.GetProjectItemTemplate("SmartStringResources.zip", "VisualBasic");
		}

		private string GetCSharpProjectItemTemplate()
		{
			return _solution.GetProjectItemTemplate("SmartStringResources.zip", "CSharp");
		}

		void _cSharpItemsEvents_ItemAdded(ProjectItem projectItem)
		{
			ItemAdded(projectItem, GetCSharpProjectItemTemplate());
		}

		void ItemAdded(ProjectItem projectItem, string itemTemplatePath)
		{
			if (Path.GetExtension(projectItem.Name) == ".resx")
			{
				var options = GetOptions();
				if (options.AutoCreate)
					AddTemplateFile(projectItem, itemTemplatePath);
			}
		}

		private ProjectItem AddTemplateFile(ProjectItem projectItem, string itemTemplatePath)
		{
			var fileName = GetFileName(projectItem);
			var templateParentProjectItems = GetParentProjectItems(projectItem);
			return templateParentProjectItems.AddFromTemplate(itemTemplatePath, fileName);
		}

		private ProjectItems GetParentProjectItems(ProjectItem projectItem)
		{
			var parent = projectItem.Collection.Parent;
			var project = parent as Project;
			if (project != null)
				return project.ProjectItems;

			var parentProjectItem = parent as ProjectItem;
			if (parentProjectItem != null)
				return parentProjectItem.ProjectItems;

			return null;
		}

		private string GetFileName(ProjectItem projectItem)
		{
			if (projectItem.FileCount == 0)
				return string.Empty;

			return Path.ChangeExtension(projectItem.FileNames[0], "tt");
		}

		private Options GetOptions()
		{
			return (Options)GetDialogPage(typeof(Options));
		}

		void DocumentEvents_DocumentSaved(Document document)
		{
			if (Path.GetExtension(document.Name) == ".resx")
			{
				var options = GetOptions();
				if (options.AutoUpdate)
				{
					ProjectItem projectItem = null;

					var templateName = Path.ChangeExtension(document.FullName, "tt");
					projectItem = document.ProjectItem.Collection.GetProjectItemByFullPath(templateName);

					if (options.AutoCreate && (projectItem == null || !File.Exists(projectItem.FileNames[0])))
					{
						try
						{
							var templateContents = GetTemplatePath(document.ProjectItem.ContainingProject.CodeModel.Language);
							projectItem = AddTemplateFile(document.ProjectItem, templateContents);
						}
						catch (NotImplementedException)
						{
						}
					}

					if (projectItem != null)
					{
						if (_sourceControl.IsItemUnderSCC(templateName) && !_sourceControl.IsItemCheckedOut(templateName))
							_sourceControl.CheckOutItem(templateName);

						projectItem.Save();
					}
				}
			}
		}

		private string GetTemplatePath(string language)
		{
			switch (language)
			{
				case CodeModelLanguageConstants.vsCMLanguageVB:
					return GetVisualBasicProjectItemTemplate();
				case CodeModelLanguageConstants.vsCMLanguageCSharp:
					return GetCSharpProjectItemTemplate();
				case "{E6FDF8BF-F3D1-11D4-8576-0002A516ECE8}":
				case CodeModelLanguageConstants.vsCMLanguageMC:
				case CodeModelLanguageConstants.vsCMLanguageVC:
				default:
					throw new NotImplementedException();
			}
		}

		#endregion

	}
}
